<script setup>
import axios from 'axios'
import { ref, defineProps, defineEmits } from 'vue'

// 支持的事件：成功和失败、关闭
const emits = defineEmits(['success', 'fail', 'close'])
// 支持的属性：类型、生成图片URL(GET)、校验URL（POST）
const props = defineProps({
    type: {
        type: String,
        default: 'RANDOM',
    },
    generateUrl: {
        type: String,
        default: 'http://localhost:8080/captcha',
    },
    validateUrl: {
        type: String,
        default: null,
    },
    log: {
        type: Boolean,
        default: false,
    },
})

// style binding
const sliderMoveBtnStyle = ref({})
const sliderImgDivStyle = ref({})
const rotateImgDivStyle = ref({})

// doc element binding
let bgImg = ref(null)
let sliderImg = ref(null)

// element props binding
let bgImgSrc = ref('')
let sliderImgSrc = ref('')

// variables
let currentCaptchaConfig = {}
let currentCaptchaId = null

const printLog = (...params) => {
    if (props.log) {
        console.log(JSON.stringify(params))
    }
}

const initConfig = (bgImageWidth, bgImageHeight, sliderImageWidth, sliderImageHeight, end) => {
    currentCaptchaConfig = {
        startTime: new Date(),
        trackArr: [],
        movePercent: 0,
        bgImageWidth,
        bgImageHeight,
        sliderImageWidth,
        sliderImageHeight,
        end,
    }
    return currentCaptchaConfig
}

const refreshCaptcha = () => {
    axios.get(`${props.generateUrl}?id=${props.type}`)
        .then(response => {
            const { data, status, statusText } = response
            if (status !== 200) {
                emits('fail', statusText)
                return
            }
            const { code, message } = data
            if (code !== 0 && code !== 200) {
                emits('fail', message)
                return
            }

            reset()

            currentCaptchaId = data.data.id
            const { backgroundImage, templateImage } = data.data.captcha
            bgImgSrc.value = backgroundImage
            sliderImgSrc.value = templateImage
            initConfig(bgImg.value.width, bgImg.value.height, sliderImg.value.width, sliderImg.value.height, 206)
        }
        ).catch(e => emits('fail', e))
}

const doDown = () => sliderMoveBtnStyle.value['background-position'] = '-5px 31.0092%'

const doMove = currentCaptchaConfig => {
    const moveX = currentCaptchaConfig.moveX
    sliderMoveBtnStyle.value['transform'] = `translate(${moveX}px, 0px)`
    sliderImgDivStyle.value['transform'] = `translate(${moveX}px, 0px)`
}

const valid = ({ bgImageWidth, bgImageHeight, sliderImageWidth, sliderImageHeight, startTime, stopTime, trackArr, }) => {
    bgImageWidth = bgImageWidth || bgImg.value.width
    bgImageHeight = bgImageHeight || bgImg.value.height
    let data = {
        bgImageWidth,
        bgImageHeight,
        sliderImageWidth,
        sliderImageHeight,
        startSlidingTime: startTime,
        entSlidingTime: stopTime,
        trackList: trackArr,
    }
    axios.post(`${props.validateUrl || props.generateUrl}?id=${currentCaptchaId}`, data)
        .then(response => {
            const { code, msg, data, success } = response.data
            if (success || code === 200 || code === 0) {
                emits('success', data)
            } else {
                refreshCaptcha()
                emits('fail', msg)
            }
        })
        .catch(e => emits('fail', e))
}

const down = event => {
    let targetTouches = event.originalEvent?.targetTouches || event.targetTouches
    let startX = event.pageX
    let startY = event.pageY
    if (startX === undefined) {
        startX = Math.round(targetTouches[0].pageX)
        startY = Math.round(targetTouches[0].pageY)
    }
    currentCaptchaConfig.startX = startX
    currentCaptchaConfig.startY = startY

    const { startX: pageX, startY: pageY, startTime, trackArr, } = currentCaptchaConfig
    trackArr.push({
        x: pageX - startX,
        y: pageY - startY,
        type: 'down',
        t: new Date().getTime() - startTime.getTime(),
    })
    printLog('start', startX, startY)

    // PC端
    window.addEventListener('mousemove', move)
    window.addEventListener('mouseup', up)
    // 手机端
    window.addEventListener('touchmove', move, false)
    window.addEventListener('touchend', up, false)

    doDown(currentCaptchaConfig)
}

const move = event => {
    if (event instanceof TouchEvent) {
        event = event.touches[0]
    }

    let pageX = Math.round(event.pageX)
    let pageY = Math.round(event.pageY)
    const { startX, startY, startTime, end, bgImageWidth, trackArr, } = currentCaptchaConfig
    let moveX = pageX - startX
    const track = {
        x: pageX - startX,
        y: pageY - startY,
        type: 'move',
        t: new Date().getTime() - startTime.getTime(),
    }
    trackArr.push(track);

    if (moveX < 0) {
        moveX = 0
    } else if (moveX > end) {
        moveX = end
    }
    currentCaptchaConfig.moveX = moveX
    currentCaptchaConfig.movePercent = moveX / bgImageWidth
    printLog('move', track)

    doMove(currentCaptchaConfig)
}

const up = event => {
    window.removeEventListener('mousemove', move)
    window.removeEventListener('mouseup', up)
    window.removeEventListener('touchmove', move)
    window.removeEventListener('touchend', up)

    if (event instanceof TouchEvent) {
        event = event.changedTouches[0]
    }

    currentCaptchaConfig.stopTime = new Date()
    let pageX = Math.round(event.pageX)
    let pageY = Math.round(event.pageY)
    const { startX, startY, startTime, trackArr, } = currentCaptchaConfig
    const track = {
        x: pageX - startX,
        y: pageY - startY,
        type: 'up',
        t: new Date().getTime() - startTime.getTime(),
    }
    trackArr.push(track)
    printLog('up', track)

    valid(currentCaptchaConfig)
}

const reset = () => {
    sliderMoveBtnStyle.value['background-position'] = '-5px 11.79625%'
    sliderMoveBtnStyle.value['transform'] = 'translate(0px, 0px)'
    sliderImgDivStyle.value['transform'] = 'translate(0px, 0px)'
    currentCaptchaId = null;
}

const close = () => emits('close')

onMounted(() => refreshCaptcha())
</script>

<template>
    <div class="slider">
        <div class="content">
            <div class="bg-img-div">
                <img ref="bgImg" id="bg-img" :src="bgImgSrc" alt />
            </div>
            <div class="slider-img-div" id="slider-img-div" :style="sliderImgDivStyle">
                <img ref="sliderImg" id="slider-img" :src="sliderImgSrc" alt />
            </div>
        </div>
        <div class="slider-move">
            <div class="slider-move-track">拖动滑块完成拼图</div>
            <div :style="sliderMoveBtnStyle" @mousedown="down" @touchstart="down" @click.prevent="login"
                class="slider-move-btn" id="slider-move-btn"></div>
        </div>
        <div class="bottom">
            <div class="close-btn" id="slider-close-btn" @click="close"></div>
            <div class="refresh-btn" id="slider-refresh-btn" @click="refreshCaptcha"></div>
        </div>
    </div>
</template>

<style scoped>
.slider {
    background-color: #fff;
    width: 278px;
    height: 285px;
    z-index: 999;
    box-sizing: border-box;
    padding: 9px;
    border-radius: 6px;
    box-shadow: 0 0 11px 0 #999999;
    user-select: none;
}

.slider .content {
    width: 100%;
    height: 159px;
    position: relative;
}

.bg-img-div {
    width: 100%;
    height: 100%;
    position: absolute;
    transform: translate(0px, 0px);
}

.slider-img-div {
    height: 100%;
    position: absolute;
    transform: translate(0px, 0px);
}

.bg-img-div img {
    width: 100%;
}

.slider-img-div img {
    height: 100%;
}

.slider .slider-move {
    height: 60px;
    width: 100%;
    margin: 11px 0;
    position: relative;
}

.slider .bottom {
    height: 19px;
    width: 100%;
}

.refresh-btn,
.close-btn,
.slider-move-track,
.slider-move-btn {
    background: url(https://static.geetest.com/static/ant/sprite.1.2.4.png) no-repeat;
}

.refresh-btn,
.close-btn {
    display: inline-block;
}

.slider-move .slider-move-track {
    line-height: 38px;
    font-size: 14px;
    text-align: center;
    white-space: nowrap;
    color: #88949d;
    -moz-user-select: none;
    -webkit-user-select: none;
    user-select: none;
}

.slider-move .slider-move-btn {
    transform: translate(0px, 0px);
    background-position: -5px 11.79625%;
    position: absolute;
    top: -12px;
    left: 0;
    width: 66px;
    height: 66px;
}

.slider-move-btn:hover,
.close-btn:hover,
.refresh-btn:hover {
    cursor: pointer
}

.bottom .close-btn {
    width: 20px;
    height: 20px;
    background-position: 0 44.86874%;
}

.bottom .refresh-btn {
    width: 20px;
    height: 20px;
    background-position: 0 81.38425%;
}
</style>